#include "grate/util/common.h"

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>

// OS-specific headers.
#if GRATE_MACOSX
# include <sys/sysctl.h>
# include <sys/types.h>
#endif

namespace growth_rate {

const size_t kBitsInByte = 8;

void Abort() {
  abort();
}

void CheckFailed(const char *condition, const char *filename, int line_no) {
  Report("CHECK failed: %s at %s:%d\n", condition, filename, line_no);
  Abort();
}

uint64_t PhysicalMemory() {
#if GRATE_MACOSX
  int mib[2] = {CTL_HW, HW_MEMSIZE};
  uint64_t cchMem = 0;
  size_t cchMemLen = sizeof(cchMem);
  if (sysctl(mib, 2, &cchMem, &cchMemLen, NULL, 0) != 0)
    return 0;
  return cchMem;
#else
  UNIMPLEMENTED();
  return 0;
#endif
}

void *Malloc(size_t size) {
  return malloc(size);
}

void Free(void *p) {
  free(p);
}

void *Realloc(void *p, size_t size) {
  return realloc(p, size);
}

bool IsPowerOfTwo(size_t x) {
  return (x > 0) && ((x & (x - 1)) == 0);
}

size_t Log2(size_t x) {
  CHECK(IsPowerOfTwo(x));
  return __builtin_ctzl(x);
}

size_t RoundUpToPowerOfTwo(size_t x) {
  CHECK(x);
  if (IsPowerOfTwo(x)) return x;
  size_t exp = __WORDSIZE - 1 - __builtin_clzl(x);
  CHECK_LT(x, 1ULL << (exp + 1));
  CHECK_GT(x, 1ULL << exp);
  return 1UL << (exp + 1);
}

static FILE *report_file;

void SetReportFile(const char *filename) {
  if (report_file && report_file != stderr)
    fclose(report_file);
  report_file = fopen(filename, "w");
}

void DebugReport(const char *msg, ...) {
#if GRATE_DEBUG == 1
  if (report_file == 0)
    report_file = stderr;
  va_list args;
  va_start(args, msg);
  vfprintf(report_file, msg, args);
  va_end(args);
  fflush(report_file);
#endif
}

void Report(const char *msg, ...) {
  if (report_file == 0)
    report_file = stderr;
  va_list args;
  va_start(args, msg);
  fprintf(report_file, "==GRATE== ");
  vfprintf(report_file, msg, args);
  va_end(args);
  fflush(report_file);
}

}  // namespace growth_rate
